define(function() {
	return function(jQuery) {
/*
 * Inline Form Validation Engine 2.5.2, jQuery plugin
 * 
 * Copyright(c) 2010, Cedric Dugas http://www.position-absolute.com
 * 
 * 2.0 Rewrite by Olivier Refalo http://www.crionics.com
 * 
 * Form validation engine allowing custom regex rules to be added. Licensed
 * under the MIT License
 */
(function($) {

			"use strict";

			var methods = {

				/**
				 * Kind of the constructor, called before any action
				 * 
				 * @param {Map}
				 *            user options
				 */
				init : function(options) {
					var form = this;
					if (!form.data('jqv') || form.data('jqv') == null) {
						options = methods._saveOptions(form, options);
						// bind all formError elements to close on click
						$(".formError").live("click", function() {
							$(this).fadeOut(150, function() {
										// remove prompt once invisible
										$(this).parent('.formErrorOuter')
												.remove();
										$(this).remove();
									});
						});

					}
					return this;
				},
				/**
				 * Attachs jQuery.validationEngine to form.submit and field.blur
				 * events Takes an optional params: a list of options ie.
				 * jQuery("#formID1").validationEngine('attach', {promptPosition :
				 * "centerRight"});
				 */
				attach : function(userOptions) {

					var form = this;
					var options;

					if (userOptions)
						options = methods._saveOptions(form, userOptions);
					else
						options = form.data('jqv');

					options.validateAttribute = (form
							.find("[data-validation-engine*=validate]").length)
							? "data-validation-engine"
							: "class";
					if (options.binded) {

						// bind fields
						form.find("[" + options.validateAttribute
								+ "*=validate]").not("[type=checkbox]")
								.not("[type=radio]").not(".datepicker").bind(
										options.validationEventTrigger,
										methods._onFieldEvent);
						form.find("[" + options.validateAttribute
								+ "*=validate][type=checkbox],["
								+ options.validateAttribute
								+ "*=validate][type=radio]").bind("click",
								methods._onFieldEvent);
						form.find("[" + options.validateAttribute
								+ "*=validate][class*=datepicker]").bind(
								options.validationEventTrigger, {
									"delay" : 300
								}, methods._onFieldEvent);
					}
					if (options.autoPositionUpdate) {
						$(window).bind("resize", {
									"noAnimation" : true,
									"formElem" : form
								}, methods.updatePromptsPosition);
					}
					// bind form.submit
					form.bind("submit", methods._onSubmitEvent);
					return this;
				},
				/**
				 * Unregisters any bindings that may point to
				 * jQuery.validaitonEngine
				 */
				detach : function() {
					var form = this;
					var options = form.data('jqv');

					// unbind fields
					form.find("[" + options.validateAttribute + "*=validate]")
							.not("[type=checkbox]").unbind(
									options.validationEventTrigger,
									methods._onFieldEvent);
					form
							.find("["
									+ options.validateAttribute
									+ "*=validate][type=checkbox],[class*=validate][type=radio]")
							.unbind("click", methods._onFieldEvent);

					// unbind form.submit
					form.unbind("submit", methods.onAjaxFormComplete);

					// unbind live fields (kill)
					form.find("[" + options.validateAttribute + "*=validate]")
							.not("[type=checkbox]").die(
									options.validationEventTrigger,
									methods._onFieldEvent);
					form.find("[" + options.validateAttribute
							+ "*=validate][type=checkbox]").die("click",
							methods._onFieldEvent);

					// unbind form.submit
					form.die("submit", methods.onAjaxFormComplete);
					form.removeData('jqv');

					if (options.autoPositionUpdate) {
						$(window).unbind("resize",
								methods.updatePromptsPosition)
					}

					return this;
				},
				/**
				 * Validates the form fields, shows prompts accordingly. Note:
				 * There is no ajax form validation with this method, only field
				 * ajax validation are evaluated
				 * 
				 * @return true if the form validates, false if it fails
				 */
				validate : function() {
					return methods._validateFields(this);
				},
				/**
				 * Validates one field, shows prompt accordingly. Note: There is
				 * no ajax form validation with this method, only field ajax
				 * validation are evaluated
				 * 
				 * @return true if the form validates, false if it fails
				 */
				validateField : function(el) {
					var options = $(this).data('jqv');
					var r = methods._validateField($(el), options);

					if (options.onSuccess && options.InvalidFields.length == 0)
						options.onSuccess();
					else if (options.onFailure
							&& options.InvalidFields.length > 0)
						options.onFailure();

					return r;
				},
				/**
				 * Validates the form fields, shows prompts accordingly. Note:
				 * this methods performs fields and form ajax validations(if
				 * setup)
				 * 
				 * @return true if the form validates, false if it fails,
				 *         undefined if ajax is used for form validation
				 */
				validateform : function() {
					return methods._onSubmitEvent.call(this);
				},
				/**
				 * Redraw prompts position, useful when you change the DOM state
				 * when validating
				 */
				updatePromptsPosition : function(event) {

					if (event && this == window) {
						var form = event.data.formElem;
						var noAnimation = event.data.noAnimation;
					} else
						var form = $(this.closest('form'));

					var options = form.data('jqv');
					// No option, take default one
					form.find('[' + options.validateAttribute + '*=validate]')
							.not(':hidden').not(":disabled").each(function() {
								var field = $(this);
								var prompt = methods._getPrompt(field);
								var promptText = $(prompt)
										.find(".formErrorContent").html();

								if (prompt)
									methods._updatePrompt(field, $(prompt),
											promptText, undefined, false,
											options, noAnimation);
							});
					return this;
				},
				/**
				 * Displays a prompt on a element. Note that the element needs
				 * an id!
				 * 
				 * @param {String}
				 *            promptText html text to display type
				 * @param {String}
				 *            type the type of bubble: 'pass' (green), 'load'
				 *            (black) anything else (red)
				 * @param {String}
				 *            possible values topLeft, topRight, bottomLeft,
				 *            centerRight, bottomRight
				 */
				showPrompt : function(promptText, type, promptPosition,
						showArrow) {

					var form = this.closest('form');
					var options = form.data('jqv');
					// No option, take default one
					if (!options)
						options = methods._saveOptions(this, options);
					if (promptPosition)
						options.promptPosition = promptPosition;
					options.showArrow = showArrow == true;

					methods._showPrompt(this, promptText, type, false, options);
					return this;
				},
				/**
				 * Closes all error prompts on the page
				 */
				hidePrompt : function() {
					var form = this;
					var options = form.data('jqv');
					var promptClass = "."
							+ methods._getClassName($(this).attr("id"))
							+ "formError";
					$(promptClass).fadeTo(options.fadeDuration, 0.3,
							function() {
								$(this).parent('.formErrorOuter').remove();
								$(this).remove();
							});
					return this;
				},
				/**
				 * Closes form error prompts, CAN be invidual
				 */
				hide : function() {
					var form = this;
					var options = form.data('jqv');
					var closingtag;
					if ($(this).is("form")) {
						closingtag = "parentForm"
								+ methods._getClassName($(this).attr("id"));
					} else {
						closingtag = methods._getClassName($(this).attr("id"))
								+ "formError";
					}
					$('.' + closingtag).fadeTo(options.fadeDuration, 0.3,
							function() {
								$(this).parent('.formErrorOuter').remove();
								$(this).remove();
							});
					return this;
				},
				/**
				 * Closes all error prompts on the page
				 */
				hideAll : function() {
					var form = this;
					var options = form.data('jqv');
					$('.formError').fadeTo(options.fadeDuration, 0.3,
							function() {
								$(this).parent('.formErrorOuter').remove();
								$(this).remove();
							});
					return this;
				},
				/**
				 * Typically called when user exists a field using tab or a
				 * mouse click, triggers a field validation
				 */
				_onFieldEvent : function(event) {
					var field = $(this);
					var form = field.closest('form');
					var options = form.data('jqv');
					// validate the current field
					window.setTimeout(function() {
								methods._validateField(field, options);
								if (options.InvalidFields.length == 0
										&& options.onSuccess) {
									options.onSuccess();
								} else if (options.InvalidFields.length > 0
										&& options.onFailure) {
									options.onFailure();
								}
							}, (event.data) ? event.data.delay : 0);

				},
				/**
				 * Called when the form is submited, shows prompts accordingly
				 * 
				 * @param {jqObject}
				 *            form
				 * @return false if form submission needs to be cancelled
				 */
				_onSubmitEvent : function() {
					var form = $(this);
					var options = form.data('jqv');

					// validate each field
					// (- skip field ajax validation, not necessary IF we will
					// perform an ajax form validation)
					var r = methods._validateFields(form,
							options.ajaxFormValidation);

					if (r && options.ajaxFormValidation) {
						methods._validateFormWithAjax(form, options);
						return false;
					}

					if (options.onValidationComplete) {
						options.onValidationComplete(form, r);
						return false;
					}
					return r;
				},
				/**
				 * Return true if the ajax field validations passed so far
				 * 
				 * @param {Object}
				 *            options
				 * @return true, is all ajax validation passed so far (remember
				 *         ajax is async)
				 */
				_checkAjaxStatus : function(options) {
					var status = true;
					$.each(options.ajaxValidCache, function(key, value) {
								if (!value) {
									status = false;
									// break the each
									return false;
								}
							});
					return status;
				},
				/**
				 * Validates form fields, shows prompts accordingly
				 * 
				 * @param {jqObject}
				 *            form
				 * @param {skipAjaxFieldValidation}
				 *            boolean - when set to true, ajax field validation
				 *            is skipped, typically used when the submit button
				 *            is clicked
				 * 
				 * @return true if form is valid, false if not, undefined if
				 *         ajax form validation is done
				 */
				_validateFields : function(form, skipAjaxValidation) {
					var options = form.data('jqv');

					// this variable is set to true if an error is found
					var errorFound = false;

					// Trigger hook, start validation
					form.trigger("jqv.form.validating");
					// first, evaluate status of non ajax fields
					var first_err = null;
					form.find('[' + options.validateAttribute + '*=validate]')
							.not(':hidden').not(":disabled").each(function() {
								var field = $(this);
								var names = [];
								if ($.inArray(field.attr('name'), names) < 0) {
									errorFound |= methods._validateField(field,
											options, skipAjaxValidation);
									if (options.doNotShowAllErrosOnSubmit)
										return false;
									if (errorFound && first_err == null) {
										first_err = field;
									}
									names.push(field.attr('name'));
								}
							});

					// second, check to see if all ajax calls completed ok
					// errorFound |= !methods._checkAjaxStatus(options);

					// third, check status and scroll the container accordingly
					form.trigger("jqv.form.result", [errorFound]);

					if (errorFound) {
						if (options.scroll) {
							var destination = first_err.offset().top;
							var fixleft = first_err.offset().left;

							// prompt positioning adjustment support. Usage:
							// positionType:Xshift,Yshift (for ex.:
							// bottomLeft:+20 or bottomLeft:-20,+10)
							var positionType = options.promptPosition;
							if (typeof(positionType) == 'string'
									&& positionType.indexOf(":") != -1)
								positionType = positionType.substring(0,
										positionType.indexOf(":"));

							if (positionType != "bottomRight"
									&& positionType != "bottomLeft") {
								var prompt_err = methods._getPrompt(first_err);
								destination = prompt_err.offset().top;
							}

							// get the position of the first error, there should
							// be at least one, no need to check this
							// var destination =
							// form.find(".formError:not('.greenPopup'):first").offset().top;
							if (options.isOverflown) {
								var overflowDIV = $(options.overflownDIV);
								if (!overflowDIV.length)
									return false;
								var scrollContainerScroll = overflowDIV
										.scrollTop();
								var scrollContainerPos = -parseInt(overflowDIV
										.offset().top);

								destination += scrollContainerScroll
										+ scrollContainerPos - 5;
								var scrollContainer = $(options.overflownDIV
										+ ":not(:animated)");

								scrollContainer.animate({
											scrollTop : destination
										}, 1100, function() {
											if (options.focusFirstField)
												first_err.focus();
										});
							} else {
								$("html:not(:animated),body:not(:animated)")
										.animate({
													scrollTop : destination,
													scrollLeft : fixleft
												}, 1100, function() {
													if (options.focusFirstField)
														first_err.focus();
												});
							}

						} else if (options.focusFirstField)
							first_err.focus();
						return false;
					}
					return true;
				},
				/**
				 * This method is called to perform an ajax form validation.
				 * During this process all the (field, value) pairs are sent to
				 * the server which returns a list of invalid fields or true
				 * 
				 * @param {jqObject}
				 *            form
				 * @param {Map}
				 *            options
				 */
				_validateFormWithAjax : function(form, options) {

					var data = form.serialize();
					var url = (options.ajaxFormValidationURL)
							? options.ajaxFormValidationURL
							: form.attr("action");
					$.ajax({
						type : "GET",
						url : url,
						cache : false,
						dataType : "json",
						data : data,
						form : form,
						methods : methods,
						options : options,
						beforeSend : function() {
							return options.onBeforeAjaxFormValidation(form,
									options);
						},
						error : function(data, transport) {
							methods._ajaxError(data, transport);
						},
						success : function(json) {
							if (json !== true) {
								// getting to this case doesn't necessary means
								// that the form is invalid
								// the server may return green or closing prompt
								// actions
								// this flag helps figuring it out
								var errorInForm = false;
								for (var i = 0; i < json.length; i++) {
									var value = json[i];

									var errorFieldId = value[0];
									var errorField = $($("#" + errorFieldId)[0]);

									// make sure we found the element
									if (errorField.length == 1) {

										// promptText or selector
										var msg = value[2];
										// if the field is valid
										if (value[1] == true) {

											if (msg == "" || !msg) {
												// if for some reason,
												// status==true and error="",
												// just close the prompt
												methods
														._closePrompt(errorField);
											} else {
												// the field is valid, but we
												// are displaying a green prompt
												if (options.allrules[msg]) {
													var txt = options.allrules[msg].alertTextOk;
													if (txt)
														msg = txt;
												}
												methods._showPrompt(errorField,
														msg, "pass", false,
														options, true);
											}
										} else {
											// the field is invalid, show the
											// red error prompt
											errorInForm |= true;
											if (options.allrules[msg]) {
												var txt = options.allrules[msg].alertText;
												if (txt)
													msg = txt;
											}
											methods._showPrompt(errorField,
													msg, "", false, options,
													true);
										}
									}
								}
								options.onAjaxFormComplete(!errorInForm, form,
										json, options);
							} else
								options.onAjaxFormComplete(true, form, "",
										options);
						}
					});

				},
				/**
				 * Validates field, shows prompts accordingly
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            field's validation rules
				 * @param {Map}
				 *            user options
				 * @return true if field is valid
				 */
				_validateField : function(field, options, skipAjaxValidation) {
					if (!field.attr("id"))
						$
								.error("jQueryValidate: an ID attribute is required for this field: "
										+ field.attr("name")
										+ " class:"
										+ field.attr("class"));

					var rulesParsing = field.attr(options.validateAttribute);
					var getRules = /validate\[(.*)\]/.exec(rulesParsing);

					if (!getRules)
						return false;
					var str = getRules[1];
					var rules = str.split(/\[|,|\]/);

					// true if we ran the ajax validation, tells the logic to
					// stop messing with prompts
					var isAjaxValidator = false;
					var fieldName = field.attr("name");
					var promptText = "";
					var required = false;
					options.isError = false;
					options.showArrow = true;

					var form = $(field.closest("form"));

					for (var i = 0; i < rules.length; i++) {
						// Fix for adding spaces in the rules
						rules[i] = rules[i].replace(" ", "")
						var errorMsg = undefined;
						switch (rules[i]) {

							case "required" :
								required = true;
								errorMsg = methods._required(field, rules, i,
										options);
								break;
							case "custom" :
								errorMsg = methods._customRegex(field, rules,
										i, options);
								break;
							case "groupRequired" :
								// Check is its the first of group, if not,
								// reload validation with first field
								// AND continue normal validation on present
								// field
								var classGroup = "["
										+ options.validateAttribute + "*="
										+ rules[i + 1] + "]";
								var firstOfGroup = form.find(classGroup).eq(0);
								if (firstOfGroup[0] != field[0]) {
									methods._validateField(firstOfGroup,
											options, skipAjaxValidation)
									options.showArrow = true;
									continue;
								}
								;
								errorMsg = methods._groupRequired(field, rules,
										i, options);
								if (errorMsg)
									required = true;
								options.showArrow = false;
								break;
							case "ajax" :
								// ajax has its own prompts handling technique
								if (!skipAjaxValidation) {
									methods._ajax(field, rules, i, options);
									isAjaxValidator = true;
								}
								break;
							case "minSize" :
								errorMsg = methods._minSize(field, rules, i,
										options);
								break;
							case "maxSize" :
								errorMsg = methods._maxSize(field, rules, i,
										options);
								break;
							case "min" :
								errorMsg = methods._min(field, rules, i,
										options);
								break;
							case "max" :
								errorMsg = methods._max(field, rules, i,
										options);
								break;
							case "past" :
								errorMsg = methods._past(field, rules, i,
										options);
								break;
							case "future" :
								errorMsg = methods._future(field, rules, i,
										options);
								break;
							case "dateRange" :
								var classGroup = "["
										+ options.validateAttribute + "*="
										+ rules[i + 1] + "]";
								var firstOfGroup = form.find(classGroup).eq(0);
								var secondOfGroup = form.find(classGroup).eq(1);

								// if one entry out of the pair has value then
								// proceed to run through validation
								if (firstOfGroup[0].value
										|| secondOfGroup[0].value) {
									errorMsg = methods._dateRange(firstOfGroup,
											secondOfGroup, rules, i, options);
								}
								if (errorMsg)
									required = true;
								options.showArrow = false;
								break;

							case "dateTimeRange" :
								var classGroup = "["
										+ options.validateAttribute + "*="
										+ rules[i + 1] + "]";
								var firstOfGroup = form.find(classGroup).eq(0);
								var secondOfGroup = form.find(classGroup).eq(1);

								// if one entry out of the pair has value then
								// proceed to run through validation
								if (firstOfGroup[0].value
										|| secondOfGroup[0].value) {
									errorMsg = methods._dateTimeRange(
											firstOfGroup, secondOfGroup, rules,
											i, options);
								}
								if (errorMsg)
									required = true;
								options.showArrow = false;
								break;
							case "maxCheckbox" :
								errorMsg = methods._maxCheckbox(form, field,
										rules, i, options);
								field = $(form.find("input[name='" + fieldName
										+ "']"));
								break;
							case "minCheckbox" :
								errorMsg = methods._minCheckbox(form, field,
										rules, i, options);
								field = $(form.find("input[name='" + fieldName
										+ "']"));
								break;
							case "equals" :
								errorMsg = methods._equals(field, rules, i,
										options);
								break;
							case "funcCall" :
								errorMsg = methods._funcCall(field, rules, i,
										options);
								break;
							case "creditCard" :
								errorMsg = methods._creditCard(field, rules, i,
										options);
								break;

							default :
								// $.error("jQueryValidator rule not
								// found"+rules[i]);
						}
						if (errorMsg !== undefined) {
							promptText += errorMsg + "<br/>";
							options.isError = true;
						}
					}
					// If the rules required is not added, an empty field is not
					// validated
					if (!required && field.val() == "")
						options.isError = false;

					// Hack for radio/checkbox group button, the validation go
					// into the
					// first radio/checkbox of the group
					var fieldType = field.prop("type");

					if ((fieldType == "radio" || fieldType == "checkbox")
							&& form.find("input[name='" + fieldName + "']")
									.size() > 1) {
						field = $(form.find("input[name='" + fieldName
								+ "'][type!=hidden]:first"));
						options.showArrow = false;
					}

					if (fieldType == "text"
							&& form.find("input[name='" + fieldName + "']")
									.size() > 1) {
						field = $(form.find("input[name='" + fieldName
								+ "'][type!=hidden]:first"));
						options.showArrow = false;
					}

					if (options.isError) {
						methods._showPrompt(field, promptText, "", false,
								options);
					} else {
						if (!isAjaxValidator)
							methods._closePrompt(field);
					}

					if (!isAjaxValidator) {
						field.trigger("jqv.field.result", [field,
										options.isError, promptText]);
					}

					/* Record error */
					var errindex = $.inArray(field[0], options.InvalidFields);
					if (errindex == -1) {
						if (options.isError)
							options.InvalidFields.push(field[0]);
					} else if (!options.isError) {
						options.InvalidFields.splice(errindex, 1);
					}

					return options.isError;
				},
				/**
				 * Required validation
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_required : function(field, rules, i, options) {
					switch (field.prop("type")) {
						case "text" :
						case "password" :
						case "textarea" :
						case "file" :
						default :

							if (!$.trim(field.val())
									|| field.val() == field
											.attr("data-validation-placeholder"))
								return options.allrules[rules[i]].alertText;
							break;
						case "radio" :
						case "checkbox" :
							var form = field.closest("form");
							var name = field.attr("name");
							if (form.find("input[name='" + name + "']:checked")
									.size() == 0) {
								if (form.find("input[name='" + name + "']")
										.size() == 1)
									return options.allrules[rules[i]].alertTextCheckboxe;
								else
									return options.allrules[rules[i]].alertTextCheckboxMultiple;
							}
							break;
						// required for <select>
						case "select-one" :
							// added by paul@kinetek.net for select boxes, Thank
							// you
							if (!field.val())
								return options.allrules[rules[i]].alertText;
							break;
						case "select-multiple" :
							// added by paul@kinetek.net for select boxes, Thank
							// you
							if (!field.find("option:selected").val())
								return options.allrules[rules[i]].alertText;
					}
				},
				/**
				 * Validate that 1 from the group field is required
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_groupRequired : function(field, rules, i, options) {
					var classGroup = "[" + options.validateAttribute + "*="
							+ rules[i + 1] + "]";
					var isValid = false;
					// Check all fields from the group
					field.closest("form").find(classGroup).each(function() {
								if (!methods._required($(this), rules, i,
										options)) {
									isValid = true;
									return false;
								}
							})

					if (!isValid)
						return options.allrules[rules[i]].alertText;
				},
				/**
				 * Validate Regex rules
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_customRegex : function(field, rules, i, options) {
					var customRule = rules[i + 1];
					var rule = options.allrules[customRule];
					if (!rule) {
						alert("jqv:custom rule not found " + customRule);
						return;
					}

					var ex = rule.regex;
					if (!ex) {
						alert("jqv:custom regex not found " + customRule);
						return;
					}
					var pattern = new RegExp(ex);

					if (!pattern.test(field.val()))
						return options.allrules[customRule].alertText;
				},
				/**
				 * Validate custom function outside of the engine scope
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_funcCall : function(field, rules, i, options) {
					var functionName = rules[i + 1];
					var fn;
					if (functionName.indexOf('.') > -1) {
						var namespaces = functionName.split('.');
						var scope = window;
						while (namespaces.length) {
							scope = scope[namespaces.shift()];
						}
						fn = scope;
					} else
						fn = window[functionName]
								|| options.customFunctions[functionName];
					if (typeof(fn) == 'function')
						return fn(field, rules, i, options);

				},
				/**
				 * Field match
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_equals : function(field, rules, i, options) {
					var equalsField = rules[i + 1];

					if (field.val() != $("#" + equalsField).val())
						return options.allrules.equals.alertText;
				},
				/**
				 * Check the maximum size (in characters)
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_maxSize : function(field, rules, i, options) {
					var max = rules[i + 1];
					var len = field.val().length;

					if (len > max) {
						var rule = options.allrules.maxSize;
						return rule.alertText + max + rule.alertText2;
					}
				},
				/**
				 * Check the minimum size (in characters)
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_minSize : function(field, rules, i, options) {
					var min = rules[i + 1];
					var len = field.val().length;

					if (len < min) {
						var rule = options.allrules.minSize;
						return rule.alertText + min + rule.alertText2;
					}
				},
				/**
				 * Check number minimum value
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_min : function(field, rules, i, options) {
					var min = parseFloat(rules[i + 1]);
					var len = parseFloat(field.val());

					if (len < min) {
						var rule = options.allrules.min;
						if (rule.alertText2)
							return rule.alertText + min + rule.alertText2;
						return rule.alertText + min;
					}
				},
				/**
				 * Check number maximum value
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_max : function(field, rules, i, options) {
					var max = parseFloat(rules[i + 1]);
					var len = parseFloat(field.val());

					if (len > max) {
						var rule = options.allrules.max;
						if (rule.alertText2)
							return rule.alertText + max + rule.alertText2;
						// orefalo: to review, also do the translations
						return rule.alertText + max;
					}
				},
				/**
				 * Checks date is in the past
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_past : function(field, rules, i, options) {

					var p = rules[i + 1];
					var pdate = (p.toLowerCase() == "now")
							? new Date()
							: methods._parseDate(p);
					var vdate = methods._parseDate(field.val());

					if (vdate > pdate) {
						var rule = options.allrules.past;
						if (rule.alertText2)
							return rule.alertText
									+ methods._dateToString(pdate)
									+ rule.alertText2;
						return rule.alertText + methods._dateToString(pdate);
					}
				},
				/**
				 * Checks date is in the future
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_future : function(field, rules, i, options) {

					var p = rules[i + 1];
					var pdate = (p.toLowerCase() == "now")
							? new Date()
							: methods._parseDate(p);
					var vdate = methods._parseDate(field.val());

					if (vdate < pdate) {
						var rule = options.allrules.future;
						if (rule.alertText2)
							return rule.alertText
									+ methods._dateToString(pdate)
									+ rule.alertText2;
						return rule.alertText + methods._dateToString(pdate);
					}
				},
				/**
				 * Checks if valid date
				 * 
				 * @param {string}
				 *            date string
				 * @return a bool based on determination of valid date
				 */
				_isDate : function(value) {
					var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/);
					return dateRegEx.test(value);
				},
				/**
				 * Checks if valid date time
				 * 
				 * @param {string}
				 *            date string
				 * @return a bool based on determination of valid date time
				 */
				_isDateTime : function(value) {
					var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/);
					return dateTimeRegEx.test(value);
				},
				// Checks if the start date is before the end date
				// returns true if end is later than start
				_dateCompare : function(start, end) {
					return (new Date(start.toString()) < new Date(end
							.toString()));
				},
				/**
				 * Checks date range
				 * 
				 * @param {jqObject}
				 *            first field name
				 * @param {jqObject}
				 *            second field name
				 * @return an error string if validation failed
				 */
				_dateRange : function(first, second, rules, i, options) {
					// are not both populated
					if ((!first[0].value && second[0].value)
							|| (first[0].value && !second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}

					// are not both dates
					if (!methods._isDate(first[0].value)
							|| !methods._isDate(second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}

					// are both dates but range is off
					if (!methods._dateCompare(first[0].value, second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}
				},
				/**
				 * Checks date time range
				 * 
				 * @param {jqObject}
				 *            first field name
				 * @param {jqObject}
				 *            second field name
				 * @return an error string if validation failed
				 */
				_dateTimeRange : function(first, second, rules, i, options) {
					// are not both populated
					if ((!first[0].value && second[0].value)
							|| (first[0].value && !second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}
					// are not both dates
					if (!methods._isDateTime(first[0].value)
							|| !methods._isDateTime(second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}
					// are both dates but range is off
					if (!methods._dateCompare(first[0].value, second[0].value)) {
						return options.allrules[rules[i]].alertText
								+ options.allrules[rules[i]].alertText2;
					}
				},
				/**
				 * Max number of checkbox selected
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_maxCheckbox : function(form, field, rules, i, options) {

					var nbCheck = rules[i + 1];
					var groupname = field.attr("name");
					var groupSize = form.find("input[name='" + groupname
							+ "']:checked").size();
					if (groupSize > nbCheck) {
						options.showArrow = false;
						if (options.allrules.maxCheckbox.alertText2)
							return options.allrules.maxCheckbox.alertText + " "
									+ nbCheck + " "
									+ options.allrules.maxCheckbox.alertText2;
						return options.allrules.maxCheckbox.alertText;
					}
				},
				/**
				 * Min number of checkbox selected
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_minCheckbox : function(form, field, rules, i, options) {

					var nbCheck = rules[i + 1];
					var groupname = field.attr("name");
					var groupSize = form.find("input[name='" + groupname
							+ "']:checked").size();
					if (groupSize < nbCheck) {
						options.showArrow = false;
						return options.allrules.minCheckbox.alertText + " "
								+ nbCheck + " "
								+ options.allrules.minCheckbox.alertText2;
					}
				},
				/**
				 * Checks that it is a valid credit card number according to the
				 * Luhn checksum algorithm.
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return an error string if validation failed
				 */
				_creditCard : function(field, rules, i, options) {
					// spaces and dashes may be valid characters, but must be
					// stripped to calculate the checksum.
					var valid = false, cardNumber = field.val().replace(/ +/g,
							'').replace(/-+/g, '');

					var numDigits = cardNumber.length;
					if (numDigits >= 14 && numDigits <= 16
							&& parseInt(cardNumber) > 0) {

						var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String();
						do {
							digit = parseInt(cardNumber.charAt(i));
							luhn += (pos++ % 2 == 0) ? digit * 2 : digit;
						} while (--i >= 0)

						for (i = 0; i < luhn.length; i++) {
							sum += parseInt(luhn.charAt(i));
						}
						valid = sum % 10 == 0;
					}
					if (!valid)
						return options.allrules.creditCard.alertText;
				},
				/**
				 * Ajax field validation
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {Array[String]}
				 *            rules
				 * @param {int}
				 *            i rules index
				 * @param {Map}
				 *            user options
				 * @return nothing! the ajax validator handles the prompts
				 *         itself
				 */
				_ajax : function(field, rules, i, options) {

					var errorSelector = rules[i + 1];
					var rule = options.allrules[errorSelector];
					var extraData = rule.extraData;
					var extraDataDynamic = rule.extraDataDynamic;

					if (!extraData)
						extraData = "";

					if (extraDataDynamic) {
						var tmpData = [];
						var domIds = String(extraDataDynamic).split(",");
						for (var i = 0; i < domIds.length; i++) {
							var id = domIds[i];
							if ($(id).length) {
								var inputValue = field.closest("form").find(id)
										.val();
								var keyValue = id.replace('#', '') + '='
										+ escape(inputValue);
								tmpData.push(keyValue);
							}
						}
						extraDataDynamic = tmpData.join("&");
					} else {
						extraDataDynamic = "";
					}

					if (!options.isError) {
						$.ajax({
							type : "GET",
							url : rule.url,
							cache : false,
							dataType : "json",
							data : "fieldId=" + field.attr("id")
									+ "&fieldValue=" + field.val()
									+ "&extraData=" + extraData + "&"
									+ extraDataDynamic,
							field : field,
							rule : rule,
							methods : methods,
							options : options,
							beforeSend : function() {
								// build the loading prompt
								var loadingText = rule.alertTextLoad;
								if (loadingText)
									methods._showPrompt(field, loadingText,
											"load", true, options);
							},
							error : function(data, transport) {
								methods._ajaxError(data, transport);
							},
							success : function(json) {

								// asynchronously called on success, data is the
								// json answer from the server
								var errorFieldId = json[0];
								// var errorField = $($("#" + errorFieldId)[0]);
								var errorField = $($("input[id='"
										+ errorFieldId + "']")[0]);

								// make sure we found the element
								if (errorField.length == 1) {
									var status = json[1];
									// read the optional msg from the server
									var msg = json[2];
									if (!status) {
										// Houston we got a problem - display an
										// red prompt
										options.ajaxValidCache[errorFieldId] = false;
										options.isError = true;

										// resolve the msg prompt
										if (msg) {
											if (options.allrules[msg]) {
												var txt = options.allrules[msg].alertText;
												if (txt)
													msg = txt;
											}
										} else
											msg = rule.alertText;

										methods._showPrompt(errorField, msg,
												"", true, options);
									} else {
										if (options.ajaxValidCache[errorFieldId] !== undefined)
											options.ajaxValidCache[errorFieldId] = true;

										// resolves the msg prompt
										if (msg) {
											if (options.allrules[msg]) {
												var txt = options.allrules[msg].alertTextOk;
												if (txt)
													msg = txt;
											}
										} else
											msg = rule.alertTextOk;

										// see if we should display a green
										// prompt
										if (msg)
											methods._showPrompt(errorField,
													msg, "pass", true, options);
										else
											methods._closePrompt(errorField);
									}
								}
								errorField.trigger("jqv.field.result", [
												errorField, options.isError,
												msg]);
							}
						});
					}
				},
				/**
				 * Common method to handle ajax errors
				 * 
				 * @param {Object}
				 *            data
				 * @param {Object}
				 *            transport
				 */
				_ajaxError : function(data, transport) {
					if (data.status == 0 && transport == null)
						alert("The page is not served from a server! ajax call failed");
					else if (typeof console != "undefined")
						console.log("Ajax error: " + data.status + " "
								+ transport);
				},
				/**
				 * date -> string
				 * 
				 * @param {Object}
				 *            date
				 */
				_dateToString : function(date) {
					return date.getFullYear() + "-" + (date.getMonth() + 1)
							+ "-" + date.getDate();
				},
				/**
				 * Parses an ISO date
				 * 
				 * @param {String}
				 *            d
				 */
				_parseDate : function(d) {

					var dateParts = d.split("-");
					if (dateParts == d)
						dateParts = d.split("/");
					return new Date(dateParts[0], (dateParts[1] - 1),
							dateParts[2]);
				},
				/**
				 * Builds or updates a prompt with the given information
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {String}
				 *            promptText html text to display type
				 * @param {String}
				 *            type the type of bubble: 'pass' (green), 'load'
				 *            (black) anything else (red)
				 * @param {boolean}
				 *            ajaxed - use to mark fields than being validated
				 *            with ajax
				 * @param {Map}
				 *            options user options
				 */
				_showPrompt : function(field, promptText, type, ajaxed,
						options, ajaxform) {
					var prompt = methods._getPrompt(field);
					// The ajax submit errors are not see has an error in the
					// form,
					// When the form errors are returned, the engine see 2
					// bubbles, but those are ebing closed by the engine at the
					// same time
					// Because no error was found befor submitting
					if (ajaxform)
						prompt = false;
					if (prompt)
						methods._updatePrompt(field, prompt, promptText, type,
								ajaxed, options);
					else
						methods._buildPrompt(field, promptText, type, ajaxed,
								options);
				},
				/**
				 * Builds and shades a prompt for the given field.
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {String}
				 *            promptText html text to display type
				 * @param {String}
				 *            type the type of bubble: 'pass' (green), 'load'
				 *            (black) anything else (red)
				 * @param {boolean}
				 *            ajaxed - use to mark fields than being validated
				 *            with ajax
				 * @param {Map}
				 *            options user options
				 */
				_buildPrompt : function(field, promptText, type, ajaxed,
						options) {

					// create the prompt
					var prompt = $('<div>');
					prompt.addClass(methods._getClassName(field.attr("id"))
							+ "formError");
					// add a class name to identify the parent form of the
					// prompt
					if (field.is(":input"))
						prompt.addClass("parentForm"
								+ methods._getClassName(field.parents('form')
										.attr("id")));
					prompt.addClass("formError");

					switch (type) {
						case "pass" :
							prompt.addClass("greenPopup");
							break;
						case "load" :
							prompt.addClass("blackPopup");
							break;
						default :
							/* it has error */
							// alert("unknown popup type:"+type);
					}
					if (ajaxed)
						prompt.addClass("ajaxed");

					// create the prompt content
					var promptContent = $('<div>').addClass("formErrorContent")
							.html(promptText).appendTo(prompt);
					// create the css arrow pointing at the field
					// note that there is no triangle on max-checkbox and radio
					if (options.showArrow) {
						var arrow = $('<div>').addClass("formErrorArrow");

						// prompt positioning adjustment support. Usage:
						// positionType:Xshift,Yshift (for ex.: bottomLeft:+20
						// or bottomLeft:-20,+10)
						var positionType = field.data("promptPosition")
								|| options.promptPosition;
						if (typeof(positionType) == 'string') {
							var pos = positionType.indexOf(":");
							if (pos != -1)
								positionType = positionType.substring(0, pos);
						}

						switch (positionType) {
							case "bottomLeft" :
							case "bottomRight" :
								prompt.find(".formErrorContent").before(arrow);
								arrow
										.addClass("formErrorArrowBottom")
										.html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
								break;
							case "topLeft" :
							case "topRight" :
								arrow
										.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
								prompt.append(arrow);
								break;
						}
					}
					// Modify z-indexes for jquery ui
					if (field.closest('.ui-dialog').length)
						prompt.addClass('formErrorInsideDialog');

					prompt.css({
								"opacity" : 0
							});
					if (options.relative) {
						// empty relative span does not disturb page layout
						// prompt positioned absolute to relative span
						// vertical-align:top so position calculations are the
						// same as isOverflown
						var outer = $('<div>').css({
									'position' : 'relative',
									'display' : 'inline',
									'overflow' : 'auto'
								}).addClass('formErrorOuter').append(prompt
								.css('position', 'absolute'));

						field.before(outer);
						if (options.relativePadding) {
							outer.css('padding-bottom', prompt.height() + 'px');
						}
					} else if (options.isOverflown) {
						// Cedric: Needed if a container is in position:relative
						// insert prompt in the form or in the overflown
						// container?
						field.before(prompt);
					} else {
						$("body").append(prompt);
					}
					var pos = methods
							._calculatePosition(field, prompt, options);
					prompt.css({
								"top" : pos.callerTopPosition,
								"left" : pos.callerleftPosition,
								"marginTop" : pos.marginTopSize,
								"opacity" : 0
							}).data("callerField", field);

					if (options.autoHidePrompt) {
						setTimeout(function() {
									prompt.animate({
												"opacity" : 0
											}, function() {
												prompt
														.closest('.formErrorOuter')
														.remove();
												prompt.remove();
											});
								}, options.autoHideDelay);
					}
					return prompt.animate({
								"opacity" : 0.87
							});
				},
				/**
				 * Updates the prompt text field - the field for which the
				 * prompt
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {String}
				 *            promptText html text to display type
				 * @param {String}
				 *            type the type of bubble: 'pass' (green), 'load'
				 *            (black) anything else (red)
				 * @param {boolean}
				 *            ajaxed - use to mark fields than being validated
				 *            with ajax
				 * @param {Map}
				 *            options user options
				 */
				_updatePrompt : function(field, prompt, promptText, type,
						ajaxed, options, noAnimation) {

					if (prompt) {
						if (typeof type !== "undefined") {
							if (type == "pass")
								prompt.addClass("greenPopup");
							else
								prompt.removeClass("greenPopup");

							if (type == "load")
								prompt.addClass("blackPopup");
							else
								prompt.removeClass("blackPopup");
						}
						if (ajaxed)
							prompt.addClass("ajaxed");
						else
							prompt.removeClass("ajaxed");

						prompt.find(".formErrorContent").html(promptText);

						var pos = methods._calculatePosition(field, prompt,
								options);
						var css = {
							"top" : pos.callerTopPosition,
							"left" : pos.callerleftPosition,
							"marginTop" : pos.marginTopSize
						};

						if (noAnimation)
							prompt.css(css);
						else
							prompt.animate(css);
					}
				},
				/**
				 * Closes the prompt associated with the given field
				 * 
				 * @param {jqObject}
				 *            field
				 */
				_closePrompt : function(field) {
					var prompt = methods._getPrompt(field);
					if (prompt)
						prompt.fadeTo("fast", 0, function() {
									prompt.parent('.formErrorOuter').remove();
									prompt.remove();
								});
				},
				closePrompt : function(field) {
					return methods._closePrompt(field);
				},
				/**
				 * Returns the error prompt matching the field if any
				 * 
				 * @param {jqObject}
				 *            field
				 * @return undefined or the error prompt (jqObject)
				 */
				_getPrompt : function(field) {
					var formId = $(field).closest('form').attr('id');
					var className = methods._getClassName(field.attr("id"))
							+ "formError";
					var match = $("." + methods._escapeExpression(className)
							+ '.parentForm' + formId)[0];
					if (match)
						return $(match);
				},
				/**
				 * Returns the escapade classname
				 * 
				 * @param {selector}
				 *            className
				 */
				_escapeExpression : function(selector) {
					return selector.replace(
							/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
				},
				/**
				 * returns true if we are in a RTLed document
				 * 
				 * @param {jqObject}
				 *            field
				 */
				isRTL : function(field) {
					var $document = $(document);
					var $body = $('body');
					var rtl = (field && field.hasClass('rtl'))
							|| (field && (field.attr('dir') || '')
									.toLowerCase() === 'rtl')
							|| $document.hasClass('rtl')
							|| ($document.attr('dir') || '').toLowerCase() === 'rtl'
							|| $body.hasClass('rtl')
							|| ($body.attr('dir') || '').toLowerCase() === 'rtl';
					return Boolean(rtl);
				},
				/**
				 * Calculates prompt position
				 * 
				 * @param {jqObject}
				 *            field
				 * @param {jqObject}
				 *            the prompt
				 * @param {Map}
				 *            options
				 * @return positions
				 */
				_calculatePosition : function(field, promptElmt, options) {

					var promptTopPosition, promptleftPosition, marginTopSize;
					var fieldWidth = field.width();
					var promptHeight = promptElmt.height();

					var overflow = options.isOverflown || options.relative;
					if (overflow) {
						// is the form contained in an overflown container?
						promptTopPosition = promptleftPosition = 0;
						// compensation for the arrow
						marginTopSize = -promptHeight;
					} else {
						var offset = field.offset();
						promptTopPosition = offset.top;
						promptleftPosition = offset.left;
						marginTopSize = 0;
					}

					// prompt positioning adjustment support
					// now you can adjust prompt position
					// usage: positionType:Xshift,Yshift
					// for example:
					// bottomLeft:+20 means bottomLeft position shifted by 20
					// pixels right horizontally
					// topRight:20, -15 means topRight position shifted by 20
					// pixels to right and 15 pixels to top
					// You can use +pixels, - pixels. If no sign is provided
					// than + is default.
					var positionType = field.data("promptPosition")
							|| options.promptPosition;
					var shift1 = "";
					var shift2 = "";
					var shiftX = 0;
					var shiftY = 0;
					if (typeof(positionType) == 'string') {
						// do we have any position adjustments ?
						if (positionType.indexOf(":") != -1) {
							shift1 = positionType.substring(positionType
									.indexOf(":")
									+ 1);
							positionType = positionType.substring(0,
									positionType.indexOf(":"));

							// if any advanced positioning will be needed
							// (percents or something else) - parser should be
							// added here
							// for now we use simple parseInt()

							// do we have second parameter?
							if (shift1.indexOf(",") != -1) {
								shift2 = shift1.substring(shift1.indexOf(",")
										+ 1);
								shift1 = shift1.substring(0, shift1
												.indexOf(","));
								shiftY = parseInt(shift2);
								if (isNaN(shiftY))
									shiftY = 0;
							};

							shiftX = parseInt(shift1);
							if (isNaN(shift1))
								shift1 = 0;

						};
					};

					if (!methods.isRTL(field)) {
						switch (positionType) {
							default :
							case "topRight" :
								if (overflow)
									promptleftPosition += fieldWidth - 30;
								else {
									promptleftPosition += fieldWidth - 30;
									promptTopPosition += -promptHeight - 2;
								}
								break;

							case "topLeft" :
								if (!overflow) {
									promptTopPosition += -promptHeight - 10;
								}
								break;

							case "centerRight" :
								if (overflow) {
									promptTopPosition = 0;
									marginTopSize = 0;
									promptleftPosition = field.outerWidth(1)
											+ 5;
								} else {
									promptleftPosition += field.outerWidth()
											+ 5;
								}
								break;
							case "centerLeft" :
								promptleftPosition -= promptElmt.width() + 2;
								if (overflow) {
									promptTopPosition = 0;
									marginTopSize = 0;
								}
								break;

							case "bottomLeft" :
								promptTopPosition = promptTopPosition
										+ field.height() + 5;
								if (overflow) {
									marginTopSize = 0;
								}
								break;
							case "bottomRight" :
								promptleftPosition += fieldWidth - 30;
								promptTopPosition += field.height() + 5;
								if (overflow) {
									marginTopSize = 0;
								}
						}
					} else {
						switch (positionType) {
							default :
							case "topLeft" :
								if (overflow)
									promptleftPosition = -(promptElmt.width()
											+ fieldWidth - 30);
								else {
									promptleftPosition -= promptElmt.width()
											- 30;
									promptTopPosition += -promptHeight - 2;
								}
								break;
							case "topRight" :
								if (overflow)
									promptleftPosition = -promptElmt.width();
								else {
									promptleftPosition += fieldWidth
											- promptElmt.width();
									promptTopPosition += -promptHeight - 2;
								}
								break;
							case "centerRight" :
								if (overflow) {
									promptTopPosition = 0;
									marginTopSize = 0;
									promptleftPosition = 5;
								} else {
									promptleftPosition += field.outerWidth()
											+ 5;
								}
								break;

							case "centerLeft" :
								if (overflow) {
									promptleftPosition = -(promptElmt.width()
											+ field.outerWidth() + 2);
									promptTopPosition = 0;
									marginTopSize = 0;
								} else {
									promptleftPosition -= promptElmt.width()
											+ 2;
								}
								break;

							case "bottomLeft" :
								if (overflow) {
									promptTopPosition = field.height() + 5;
									promptleftPosition = -(promptElmt.width()
											+ fieldWidth - 30);
									marginTopSize = 0;
								} else {
									promptleftPosition += -promptElmt.width()
											+ 30;
									promptTopPosition = promptTopPosition
											+ field.height() + 15;
								}
								break;

							case "bottomRight" :
								if (overflow) {
									promptTopPosition = field.height() + 5;
									promptleftPosition = -promptElmt.width();
									marginTopSize = 0;
								} else {
									promptleftPosition += fieldWidth
											- promptElmt.width();
									promptTopPosition += field.height() + 15;
								}
						}
					}

					// apply adjusments if any
					promptleftPosition += shiftX;
					promptTopPosition += shiftY;

					return {
						"callerTopPosition" : promptTopPosition + "px",
						"callerleftPosition" : promptleftPosition + "px",
						"marginTopSize" : marginTopSize + "px"
					};
				},
				/**
				 * Saves the user options and variables in the form.data
				 * 
				 * @param {jqObject}
				 *            form - the form where the user option should be
				 *            saved
				 * @param {Map}
				 *            options - the user options
				 * @return the user options (extended from the defaults)
				 */
				_saveOptions : function(form, options) {

					// is there a language localisation ?
					if ($.validationEngineLanguage)
						var allRules = $.validationEngineLanguage.allRules;
					else
						$
								.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
					// --- Internals DO NOT TOUCH or OVERLOAD ---
					// validation rules and i18
					$.validationEngine.defaults.allrules = allRules;

					var userOptions = $.extend(true, {},
							$.validationEngine.defaults, options);

					// Needed to be retro compatible
					if (userOptions.isOverflown)
						userOptions.relative = true;
					if (userOptions.relative)
						userOptions.isOverflown = true;

					form.data('jqv', userOptions);
					return userOptions;
				},

				/**
				 * Removes forbidden characters from class name
				 * 
				 * @param {String}
				 *            className
				 */
				_getClassName : function(className) {
					if (className)
						return className.replace(/:/g, "_").replace(/\./g, "_");
				}
			};

			/**
			 * Plugin entry point. You may pass an action as a parameter or a
			 * list of options. if none, the init and attach methods are being
			 * called. Remember: if you pass options, the attached method is NOT
			 * called automatically
			 * 
			 * @param {String}
			 *            method (optional) action
			 */
			$.fn.validationEngine = function(method) {

				var form = $(this);
				if (!form[0])
					return false; // stop here if the form does not exist

				if (typeof(method) == 'string' && method.charAt(0) != '_'
						&& methods[method]) {

					// make sure init is called once
					if (method != "showPrompt" && method != "hidePrompt"
							&& method != "hide" && method != "hideAll")
						methods.init.apply(form);

					return methods[method].apply(form, Array.prototype.slice
									.call(arguments, 1));
				} else if (typeof method == 'object' || !method) {

					// default constructor with or without arguments
					methods.init.apply(form, arguments);
					return methods.attach.apply(form);
				} else {
					$.error('Method ' + method
							+ ' does not exist in jQuery.validationEngine');
				}
			};

			// LEAK GLOBAL OPTIONS
			$.validationEngine = {
				defaults : {

					// Name of the event triggering field validation
					validationEventTrigger : "blur",
					// Automatically scroll viewport to the first error
					scroll : true,
					// Focus on the first input
					focusFirstField : true,
					// Opening box position, possible locations are: topLeft,
					// topRight, bottomLeft, centerRight, bottomRight
					promptPosition : "topRight",
					bindMethod : "bind",
					// internal, automatically set to true when it parse a _ajax
					// rule
					inlineAjax : false,
					// if set to true, the form data is sent asynchronously via
					// ajax to the form.action url (get)
					ajaxFormValidation : false,
					// Ajax form validation callback method: boolean
					// onComplete(form, status, errors, options)
					// retuns false if the form.submit event needs to be
					// canceled.
					ajaxFormValidationURL : false,
					// The url to send the submit ajax validation (default to
					// action)
					onAjaxFormComplete : $.noop,
					// called right before the ajax call, may return false to
					// cancel
					onBeforeAjaxFormValidation : $.noop,
					// Stops form from submitting and execute function
					// assiciated with it
					onValidationComplete : false,

					// better relative positioning
					relative : false,
					// insert spacing when error prompts inserted if relative =
					// True and relativePadding = True
					// Use it if you want to have your prompts below your field
					// integrated
					relativePadding : false,
					// Used when the form is displayed within a scrolling DIV
					isOverflown : false,
					overflownDIV : "",

					// Used when you have a form fields too close and the errors
					// messages are on top of other disturbing viewing messages
					doNotShowAllErrosOnSubmit : false,

					// true if you want to vind the input fields
					binded : true,
					// set to true, when the prompt arrow needs to be displayed
					showArrow : true,
					// did one of the validation fail ? kept global to stop
					// further ajax validations
					isError : false,
					// Caches field validation status, typically only bad status
					// are created.
					// the array is used during ajax form validation to detect
					// issues early and prevent an expensive submit
					ajaxValidCache : {},
					// Auto update prompt position after window resize
					autoPositionUpdate : false,

					InvalidFields : [],
					onSuccess : false,
					onFailure : false,
					// Auto-hide prompt
					autoHidePrompt : false,
					// Delay before auto-hide
					autoHideDelay : 10000,
					// Fade out duration while hiding the validations
					fadeDuration : 0.3
				}
			};
			$(function() {
						$.validationEngine.defaults.promptPosition = methods
								.isRTL() ? 'topLeft' : "topRight"
					});
		})(jQuery);
	}
});
